package org.web.base.helper;

import org.apache.log4j.Logger;
import org.springframework.beans.BeanUtils;

import java.lang.reflect.Method;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.function.Function;

public class BeanConvertHelper {

    private static final Logger logger = Logger.getLogger(BeanConvertHelper.class);

    // 缓存类的setter方法， 两层缓存，第一层缓存的KEY是类名称，第二层缓存的KEY是属性名称, KEY 是属性名称的小写, 目的是为了忽略大小写，VALUE是setter方法。
    private static final Map<Class<?>, Map<String, Method>> setterMethodCache = new HashMap<>();
    // 缓存方法的Map，key是类，value是属性名与对应getter方法的映射
    private static final Map<Class<?>, Map<String, Method>> getterMethodCache = new HashMap<>();

    private static final Map<Class<?>, Function<String, ?>> customConverters = new HashMap<>();
    private static final String[] dateFormats = new String[]{"yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd", "MM/dd/yyyy", "dd-MM-yyyy"};

    /**
     * 将Map转换为指定的Bean对象返回。
     *
     * @param map Map对象，key为属性名称，value为属性值。key的大小写不敏感， 会自动转换为小写进行匹配。
     * @param clazz 目标Bean类
     * @param <T> 目标Bean类型
     * @return 转换后的Bean对象
     * */
    public static <T> T convertMapToBean(Map<String, Object> map, Class<T> clazz) throws Exception {
        T obj = clazz.getDeclaredConstructor().newInstance();
        Map<String, Method> setters = getSettersByClass(clazz);
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            String propertyName = entry.getKey();
            Object value = entry.getValue();
            Method setterMethod = setters.get(toLowerCase(propertyName));
            if (setterMethod != null) {
                Class<?> paramType = setterMethod.getParameterTypes()[0];
                if (value != null) {
                    if (value.getClass() == String.class) {
                        // 把字符串转换为目标类型
                        value = convertType((String) value, paramType);
                    } else if (value instanceof Map && !Map.class.isAssignableFrom(paramType)) {
                        value = convertMapToBean((Map<String, Object>) value, paramType);
                    } else if (value instanceof List && Set.class.isAssignableFrom(paramType)) {
                        value = new HashSet<>((List)value);
                    }
                }
                setterMethod.invoke(obj, value);
            }
        }
        return obj;
    }

    /**
     * 如果属性是时间，需要注意可能会损失精度。
     * @param bean 目标Bean对象,其中针对时间类型的属性，特别处理，按字符串格式输出。
     * @return Map对象，key为属性名称，value为属性值。
     * */
    public static <T> Map<String, Object> convertBeanToMap(T bean) throws Exception {
        Map<String, Object> map = new HashMap<>();
        for (Method method : bean.getClass().getMethods()) {
            if (method.getName().startsWith("get") && method.getParameterCount() == 0) {
                String propertyName = method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4);
                Object value = method.invoke(bean);
                if (value instanceof Date) {
                    value = formatDate((Date) value);
                }
                map.put(propertyName, value);
            }
        }
        return map;
    }

    private static String formatDate(Date date) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        boolean hasTime = calendar.get(Calendar.HOUR_OF_DAY) != 0 || calendar.get(Calendar.MINUTE) != 0 || calendar.get(Calendar.SECOND) != 0;
        String format = hasTime ? dateFormats[0] : dateFormats[1];
        return new SimpleDateFormat(format).format(date);
    }

    public static <S, T> List<T> convertListToList(List<S> sourceList, Class<T> targetClass) {
        List<T> targetList = new ArrayList<>();
        if (null == sourceList) {
            return targetList;
        }
        for (S sourceObject : sourceList) {
            try {
                T targetObject = targetClass.getDeclaredConstructor().newInstance();
                if (sourceObject != null && sourceObject instanceof Map) {
                    targetObject = convertMapToBean((Map<String, Object>) sourceObject, targetClass);
                } else if (sourceObject != null) {
                    BeanUtils.copyProperties(sourceObject, targetObject);
                }
                targetList.add(targetObject);
            } catch (Exception e) {
                throw new RuntimeException("Failed to convertListToList: " + e.getMessage(), e);
            }
        }
        return targetList;
    }

    public static Map<String, Method> getSettersByClass(Class<?> clazz) {
        // 检查缓存中是否已经存在
        if (setterMethodCache.containsKey(clazz)) {
            return setterMethodCache.get(clazz);
        }
        Map<String, Method> setters = new HashMap<>();
        for (Method method : clazz.getMethods()) {
            if (method.getName().startsWith("set") && method.getParameterCount() == 1) {
                String propertyName = method.getName().substring(3).toLowerCase();
                // 统一转换为小写进行缓存
                setters.put(propertyName.toLowerCase(), method);
            }
        }
        setterMethodCache.put(clazz, setters);
        return setters;
    }

    private static Map<String,Method> getGettersByClass(Class<?> clazz) {
        // 检查缓存中是否已经存在
        if (getterMethodCache.containsKey(clazz)) {
            return getterMethodCache.get(clazz);
        }
        Map<String,Method> getters = new HashMap<>();
        for (Method method : clazz.getMethods()) {
            if (method.getName().startsWith("get") && method.getParameterCount() == 0) {
                String propertyName = method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4);
                getters.put(propertyName, method);
            }
        }
        getterMethodCache.put(clazz, getters);
        return getters;
    }


    private static Object convertType(String value, Class<?> targetType) {
        if (getConverters().containsKey(targetType)) {
            return customConverters.get(targetType).apply(value);
        }
        if (targetType == Integer.class || targetType == int.class) {
            return Integer.parseInt(value);
        } else if (targetType == Long.class || targetType == long.class) {
            return Long.parseLong(value);
        } else if (targetType == Double.class || targetType == double.class) {
            return Double.parseDouble(value);
        } else if (targetType == Float.class || targetType == float.class) {
            return Float.parseFloat(value);
        } else if (targetType == Boolean.class || targetType == boolean.class) {
            return Boolean.parseBoolean(value);
        } else if (targetType == Character.class || targetType == char.class) {
            return value.charAt(0);
        } else if (targetType == Date.class) {
            return parseDate(value);
        } else if (targetType == LocalDateTime.class) {
            return parseLocalDateTime(value);
        } else if (Collection.class.isAssignableFrom(targetType)) {
            return convertToCollection(value, targetType);
        } else if (Map.class.isAssignableFrom(targetType)) {
            // For nested Map
            return value;  // Return as is if the target type is a Map
        }
        return value;  // 返回原始字符串，如果无法转换为目标类型
    }

    private static Map<Class<?>, Function<String, ?>> getConverters() {
        return customConverters;
    }

    private static Date parseDate(String value) {
        for (String format : dateFormats) {
            try {
                return new SimpleDateFormat(format).parse(value);
            } catch (ParseException e) {
                // 继续尝试下一个格式
            }
        }
        throw new IllegalArgumentException("Unparseable date: " + value);
    }

    private static LocalDateTime parseLocalDateTime(String value) {
        for (String format : dateFormats) {
            try {
                DateTimeFormatter dtf = DateTimeFormatter.ofPattern(format);
                return LocalDateTime.parse(value, dtf);
            } catch (Exception e) {
                // 继续尝试下一个格式
            }
        }
        logger.debug("Unparseable date-time: " + value);
        return null;
    }

    private static Collection<?> convertToCollection(String value, Class<?> targetType) {
        // Assume value is a comma-separated list for simplicity
        String[] values = value.split(",");
        Collection<Object> collection;
        if (List.class.isAssignableFrom(targetType)) {
            collection = new ArrayList<>();
        } else if (Set.class.isAssignableFrom(targetType)) {
            collection = new HashSet<>();
        } else {
            throw new IllegalArgumentException("Unsupported collection type: " + targetType);
        }
        for (String val : values) {
            collection.add(val.trim()); // add each value after trimming
        }
        return collection;
    }

    private static String toLowerCase(String str) {
        return str.toLowerCase();
    }

}
